package com.atguigu.springsecurity.filter;

import com.atguigu.springsecurity.util.JwtUtil;
import com.atguigu.springsecurity.util.RedisCache;
import com.atguigu.springsecurity.vo.LoginUserVo;
import io.jsonwebtoken.Claims;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.annotation.Resource;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Collection;
import java.util.List;

/**
 * 原本我们应该去实现Filter, 但是因为我们默认的过滤器接口存在问题(servlet版本不同 一个请求过来过滤器会被调用多次)
 */
// 保证这个OncePerRequestFilter每一个请求只请求一次过滤器

// 现在我们只是将过滤器放在容器当中，但是没有配置在哪里进行拦截

@Component // 因为我在添加过滤器的时候使用的是new的方法
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {

    // 注意因为我们需要spring容器里面的东西所以我们需要
    // 将这个弄在容器里面 所以我们过滤器里面就不能使用new的形式了
    @Resource
    private RedisCache redisCache;

    @Override
    protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {
        // 获取token
        String token = httpServletRequest.getHeader("token");

        if(StringUtils.isEmpty(token)) {
            // 如果为空
            // 放行
            filterChain.doFilter(httpServletRequest, httpServletResponse);
            // 注意如果是登录接口才会继续执行，否则就直接返回403
            return;
        }

        // 解析token
        String subject;
        try {
            Claims claims = JwtUtil.parseJWT(token);
            // 这个subject就是userId
            subject = claims.getSubject();
        } catch (Exception e) {
            throw new RuntimeException("token非法");
//            e.printStackTrace();
        }

        // 从redis中根据userId获取信息
        String userId = subject;
        String redisKey = "login:" + userId;
        LoginUserVo res = redisCache.getCacheObject(redisKey);

        if(res == null) {
            throw new RuntimeException("登录超时");
        }

        // 存入SecurityContextHolder
        // 这里必须要使用三个参数的 因为里面第三个参数会设置认证状态为true
        /*
            super(authorities);
            this.principal = principal;
            this.credentials = credentials;
            super.setAuthenticated(true); 就是这个
            就不需要去providerManager去认证了
         */
        // TODO 获取权限信息封装到Authentication
        Collection<? extends GrantedAuthority> authorities = res.getAuthorities();
        UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(res, null, authorities);
        SecurityContextHolder.getContext().setAuthentication(authenticationToken);
        // 放行
        filterChain.doFilter(httpServletRequest, httpServletResponse);
    }
}
